package jf.states;

import jf.closure.F3;
import jf.states.graph.GraphDescription;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Simple state processor with object holder<br>
 * This is thread-safe realisation. With synchronisation on process object.<br>
 * Please notice, if you remove object while action processing, action will be finished.   
 * @author ivanalx
 * @date 06.05.2009 16:52:23
 */
public class StateProcessor<S, A, T> {
	private final GraphDescription<S, A, T> description;

	private final Map<T, StateHolder<S>> objects;

	public StateProcessor(GraphDescription<S, A, T> description) {
		this.description = description;
		objects = new ConcurrentHashMap<T, StateHolder<S>>();
	}

	/**
	 * Add object to graph with inital state.
	 * @param object object for adding
	 * @param initalStage inital stage for adding
	 */
	public void putToGraph(T object, S initalStage) {
		objects.put(object, createHolder(initalStage));
	}

	/**
	 * Remove object from processor
	 * @param object object for remove
	 * @return current state for removing object
	 */
	public S removeFromGraph(T object) {
		StateHolder<S> stateHolder = objects.remove(object);
		return stateHolder != null? stateHolder.getState(): null;
	}

	/**
	 * Process action on object.
	 * @param o object for action process
	 * @param action action for process
	 * @return new state of object
	 * @throws NoValidEdgeForStateAndAction given action for this state is not exists
	 * @throws NoSuchObjectInProcessor there is no object in processor
	 */
	public S processAction(T o, A action) {
		final StateHolder<S> stateHolder = getHolderForObject(o);
		if (stateHolder != null) {
			synchronized (stateHolder) {
				F3<S,A,T,S> f3 = description.getFunction(stateHolder.getState(), action);
				if (f3 == null) {
					throw new NoValidEdgeForStateAndAction(stateHolder.getState(), action);
				}
				S s = f3.apply(stateHolder.getState(), action, o);
				stateHolder.setState(s);
				return s;
			}
		} else {
			throw new NoSuchObjectInProcessor();
		}
	}

	public StateHolder<S> getHolderForObject(T o) {
		return objects.get(o);
	}

	protected StateHolder<S> createHolder(S initalStage) {
		return StateHolder.create(initalStage);
	}

	protected static class StateHolder<S> {
		public StateHolder() {
		}

		public static <S> StateHolder<S> create(S s) {
			return new StateHolder<S>(s); 
		}

		private S state;

		private StateHolder(S state) {
			this.state = state;
		}

		public S getState() {
			return state;
		}

		public void setState(S state) {
			this.state = state;
		}
	}
}
